package com.ywdnf.gomoku.model.chess;

import com.ywdnf.gomoku.util.Const;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
import lombok.Getter;
import lombok.Setter;

/**
 * 棋盘布局
 *
 * @author lss
 * @date 2019-06-25, Tue
 */
@Getter
@Setter
public class GomokuLayout {

  private static final int WIN_COUNT = 5;

  private int rowCount;

  private int colCount;

  /**
   * 是否结束，已经有人获胜
   */
  private boolean finish;

  /**
   * 当前布局
   */
  private Chessman[][] curLay;

  /**
   * 获胜玩家
   */
  private Player p;

  /**
   * 一共下了多少个棋
   */
  private int countAll;

  /**
   * 上次修改时间
   */
  private long lastModified;

  /**
   * 空白棋子
   */
  private List<Point> freePoints = new ArrayList<>();

  /**
   * 备忘录，用于悔棋
   */
  private Stack<Point> memo = new Stack<>();

  GomokuLayout() {
    this(Const.GOMOKU_LAYOUT_ROW_COUNT, Const.GOMOKU_LAYOUT_COL_COUNT);
  }

  private GomokuLayout(int rowCount, int colCount) {
    this.rowCount = rowCount;
    this.colCount = colCount;
    curLay = new Chessman[rowCount][colCount];
    for (int i = 0; i < rowCount; i++) {
      for (int j = 0; j < colCount; j++) {
        Point point = new Point(i, j);
        curLay[i][j] = new Chessman(point, null);
        freePoints.add(point);
      }
    }
    lastModified = System.currentTimeMillis();
  }

  /**
   * 修改棋子状态
   *
   * @param c 棋子坐标
   * @param state 棋子需要修改的状态
   * @param p 哪个玩家的操作
   */
  public boolean modChessman(Point c, ChessState state, Player p, boolean add) {
    lastModified = System.currentTimeMillis();
    int i = c.x;
    int j = c.y;
    // for (int i = 0; i < rowCount; i++) {
    //   for (int j = 0; j < colCount; j++) {
    //     if (c.getX() == i && c.getY() == j) {
    if (!add) {
      curLay[i][j].setState(state);
      curLay[i][j].setP(null);
      freePoints.add(c);
      // p.getMyPoints().remove(c);
      // memo自行操作
      countAll--;
    } else {
      curLay[i][j].setState(state);
      curLay[i][j].setP(p);
      freePoints.remove(c);
      p.getMyPoints().add(c);
      memo.push(c);
      countAll++;
      isFinishBecauseThis(curLay[i][j], p);
    }
    return true;
    // }
    //   }
    // }
    // return false;
  }

  public List<Point> backToLastMove(Player withdrawUser) {
    lastModified = System.currentTimeMillis();
    List<Point> withDrawPoints = new ArrayList<>(4);
    if (memo.size() == 0) {
      return withDrawPoints;
    }
    int i = 10;
    boolean canWithDraw = false;
    List<Point> backup = new ArrayList<>();
    while (true) {
      if (i-- == 0 || memo.size() == 0) {
        // 防止死循环
        return new ArrayList<>();
      }
      Point p = memo.pop();
      backup.add(p);
      withDrawPoints.add(p);
      if (curLay[p.x][p.y].getP().equals(withdrawUser)) {
        canWithDraw = true;
        break;
      }
    }
    if (!canWithDraw) {
      if (!backup.isEmpty()) {
        for (int i1 = backup.size() - 1; i1 >= 0; i1--) {
          // 倒序放进去
          memo.push(backup.get(i1));
        }
      }
      return new ArrayList<>();
    }
    if (!withDrawPoints.isEmpty()) {
      withDrawPoints.forEach(p -> modChessman(p, ChessState.UNPLACED, null, false));
    }
    return withDrawPoints;
  }


  /**
   * 检查是否落子后有玩家获胜
   *
   * @param cm 落子
   * @param p 落子玩家
   */
  private void isFinishBecauseThis(Chessman cm, Player p) {
    if (countAll < 9) {
      return;
    }
    Point c = cm.getPoint();
    int count = 1;
    // 行向左有几个相同颜色的棋子
    for (int i = c.getY() - 1; i >= 0; i--) {
      if (p.equals(curLay[c.getX()][i].getP())) {
        count++;
      } else {
        break;
      }
    }
    // 行向右有几个相同颜色的棋子
    for (int i = c.getY() + 1; i < colCount; i++) {
      if (p.equals(curLay[c.getX()][i].getP())) {
        count++;
      } else {
        break;
      }
    }
    if (count >= WIN_COUNT) {
      finish = true;
      this.p = p;
      return;
    }

    count = 1;
    // 列向上有几个相同颜色的棋子
    for (int i = c.getX() - 1; i >= 0; i--) {
      if (p.equals(curLay[i][c.getY()].getP())) {
        count++;
      } else {
        break;
      }
    }
    // 列向下有几个相同颜色的棋子
    for (int i = c.getX() + 1; i < rowCount; i++) {
      if (p.equals(curLay[i][c.getY()].getP())) {
        count++;
      } else {
        break;
      }
    }
    if (count >= WIN_COUNT) {
      finish = true;
      this.p = p;
      return;
    }

    count = 1;
    // 正对角线向上
    int i, j;
    for (i = c.getX() - 1, j = c.getY() - 1; i >= 0 && j >= 0; i--, j--) {
      if (p.equals(curLay[i][j].getP())) {
        count++;
      } else {
        break;
      }
    }
    // 正对角线向下
    for (i = c.getX() + 1, j = c.getY() + 1; i < rowCount && j < colCount; i++, j++) {
      if (p.equals(curLay[i][j].getP())) {
        count++;
      } else {
        break;
      }
    }
    if (count >= WIN_COUNT) {
      finish = true;
      this.p = p;
      return;
    }

    count = 1;
    // 反对角线向上
    for (i = c.getX() - 1, j = c.getY() + 1; i >= 0 && j < colCount; i--, j++) {
      if (p.equals(curLay[i][j].getP())) {
        count++;
      } else {
        break;
      }
    }
    // 反对角线向下
    for (i = c.getX() + 1, j = c.getY() - 1; i < rowCount && j >= 0; i++, j--) {
      if (p.equals(curLay[i][j].getP())) {
        count++;
      } else {
        break;
      }
    }
    if (count >= WIN_COUNT) {
      finish = true;
      this.p = p;
      return;
    }
    finish = false;
  }

}
